package com.kinyx.framework.file;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.ReturnNull;
import com.kinyx.framework.annotation.assist.Tester;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.reflect.KBeanUtils;
import com.kinyx.framework.utils.KArrayUtils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 属性文件读取工具类
 */
@Tester("com.kinyx.framework.file.KPropertiesReader_Tester_")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KPropertiesReader {

	/**
	 * 读取配置文件
	 * @param fileName 文件名（相对路径）
	 * @return 属性对象
	 */
	@ReturnNotNull
	@Throws(at = "KReader.read(InputStream, String)", value = "UnsupportedEncodingException", when = "new InputStreamReader(InputStream, String)")
	@Throws(at = "KReader.read(InputStream, String)", value = "IOException", when = "InputStreamReader.read()")
	@Throws(at = "KFileReader.openStream(String)", value = "ErrorCode0.FileDoesNotExists", when = "ClassLoader.getSystemResource(String) == null")
	@Throws(at = "KFileReader.openStream(String)", value = "IOException", when = "URL.openStream()")
	@Throws(at = "KPropertiesReader.read(String, String)", value = "IOException", when = "Properties.load(Reader)")
	public static Properties read(@CanNotNull final String fileName) {
		final String content = KFileReader.read(fileName, "UTF-8");
		final Properties properties = new Properties();
		try {
			properties.load(new StringReader(content));
		} catch (final IOException e) {
			throw new KRuntimeException(e);
		}
		return properties;
	}

	private static <T> T getValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final T defaultValue, final Function<String, T> converter) {
		final String value = properties.getProperty(key);
		if (value == null) { return defaultValue; } // 未配置，返回默认值。
		if (value.equals("")) { return defaultValue; } // 配置了键，未配置值，返回默认值。
		if (value.equals("null")) { return null; } // 配置了键，配置的值指定为null，返回null。
		return converter.apply(value);
	}

	/**
	 * 取String值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return String值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static String getStringValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final String defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return value;
		});
	}

	/**
	 * 取Byte值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Byte值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Byte getByteValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Byte defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Byte.parseByte(value);
		});
	}

	/**
	 * 取Short值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Short值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Short getShortValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Short defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Short.parseShort(value);
		});
	}

	/**
	 * 取Integer值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Integer值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Integer getIntegerValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Integer defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Integer.parseInt(value);
		});
	}

	/**
	 * 取Long值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Long值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Long getLongValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Long defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Long.parseLong(value);
		});
	}

	/**
	 * 取Float值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Float值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Float getFloatValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Float defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Float.parseFloat(value);
		});
	}

	/**
	 * 取Double值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Double值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Double getDoubleValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Double defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Double.parseDouble(value);
		});
	}

	/**
	 * 取Boolean值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Boolean值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Boolean getBooleanValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Boolean defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			return Boolean.parseBoolean(value);
		});
	}

	/**
	 * 取List值
	 * <pre>
	 * 配置文件内容：
	 * 	list1=foo,bar,qux
	 * 传入参数：
	 * 	key == "list1"
	 * 	separator == ","
	 * 读取结果：
	 * 	[foo, bar, qux]
	 * 
	 * 配置文件内容：
	 * 	list2=foo|bar|qux
	 * 传入参数：
	 * 	key == "list2"
	 * 	separator == "|"
	 * 读取结果：
	 * 	[foo, bar, qux]
	 * </pre>
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @param separator 分隔符
	 * @return List值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static List<String> getListValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final List<String> defaultValue, @CanNull(defaultValue = ",") final String separator) {
		if (separator == null) { return getListValue(properties, key, defaultValue, ","); }
		return getValue(properties, key, defaultValue, value -> {
			return KArrayUtils.toList(value.split(separator));
		});
	}

	/**
	 * 取Map值
	 * <pre>
	 * 配置文件内容：
	 * 	map1=foo=1&amp;bar=2
	 * 传入参数：
	 * 	key == "map1"
	 * 	sectionSeparator == "&amp;"
	 * 	keyValueSeparator == "="
	 * 读取结果：
	 * 	{foo: 1, bar: 2}
	 * 
	 * 配置文件内容：
	 * 	map2=foo-1,bar-2
	 * 传入参数：
	 * 	key == "map2"
	 * 	sectionSeparator == ","
	 * 	keyValueSeparator == "-"
	 * 读取结果：
	 * 	{foo: 1, bar: 2}
	 * </pre>
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @param sectionSeparator 段分隔符
	 * @param keyValueSeparator 键值对分隔符
	 * @return Map值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	public static Map<String, String> getMapValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Map<String, String> defaultValue, @CanNull(defaultValue = "&") final String sectionSeparator, @CanNull(defaultValue = "=") final String keyValueSeparator) {
		if (sectionSeparator == null) { return getMapValue(properties, key, defaultValue, "&", keyValueSeparator); }
		if (sectionSeparator.equals("|")) { return getMapValue(properties, key, defaultValue, "\\|", keyValueSeparator); }
		if (keyValueSeparator == null) { return getMapValue(properties, key, defaultValue, sectionSeparator, "="); }
		if (keyValueSeparator.equals("|")) { return getMapValue(properties, key, defaultValue, sectionSeparator, "\\|"); }
		return getValue(properties, key, defaultValue, value -> {
			final Map<String, String> map = new HashMap<>();
			final String[] sections = value.split(sectionSeparator);
			for (final String section : sections) {
				final String[] keyValues = section.split(keyValueSeparator);
				map.put(keyValues[0], keyValues[1]);
			}
			return map;
		});
	}

	/**
	 * 取Class值
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return Class值
	 */
	@ReturnNull("未配置该属性，且defaultValue==null")
	@ReturnNull("配置的属性值为''，且defaultValue==null")
	@ReturnNull("配置的属性值为'null'")
	@ReturnNull("Class.forName(value)异常，且defaultValue==null")
	public static Class<?> getClassValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final Class<?> defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			try {
				return Class.forName(value);
			} catch (final ClassNotFoundException e) {
				return defaultValue;
			}
		});
	}

	/**
	 * 取实例值
	 * @param <T> 泛型
	 * @param properties 属性
	 * @param key 配置键
	 * @param defaultValue 默认值
	 * @return 实例值
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getInstanceValue(@CanNotNull final Properties properties, @CanNotNull final String key, @CanNull final T defaultValue) {
		return getValue(properties, key, defaultValue, value -> {
			try {
				return (T) KBeanUtils.newInstance(Class.forName(value));
			} catch (final ClassNotFoundException e) {
				return defaultValue;
			}
		});
	}

}
